﻿module Environment
open System
open EmitType
open SymbolTableTypes
open StateMonad


type Environment =
  {
    GlobalSymbols     : GlobalSymbols
    NumTabs           : int
//    print_string      : string -> Unit
    print_string_nl   : string -> Unit
    Consts            : ResizeArray<string>
    //IsMethodVoid      : bool
    mutable ActualArgs        : int
  }

type State<'a> = Environment -> ('a * Environment)

type Offset = int

type Mem = Mem of string
  with 
    member this.GetValue = 
      let (Mem value) = this
      value

    member this.Ptr =
      let (Mem value) = this
      "[ " + value + " ]"

type Const = Const of string
  with 
    member this.GetValue = 
      let (Const value) = this
      value
    static member OfValue int =
      Const (string int)
    static member One =
      Const "1"
    static member MinusOne =
      Const "-1"
    static member Four =
      Const "4"
      
type Ptr = Ptr of string

type Reg = Reg of string
  with 
      member this.GetValue = 
        let (Reg value) = this
        value
              
      member this.PtrWithOffset (offset : Offset) =
        let (Reg value) = this
        if offset <> 0 then
          let sign = if offset >= 0 then "+" else "+-" 
          let offset = Math.Abs offset |> string
          "[" + value + sign  + offset + " "  + "]"
        else
          "[" + value + "]"

      member this.PtrSum (reg : Reg) =
        let (Reg value1) = this
        let (Reg value2) = reg
        "[" + value1 + " + " + value2 + " "  + "]"

      member this.PtrSumWithOffset (reg : Reg) (offset : Const) =
        let (Reg value1) = this
        let (Reg value2) = reg
        Ptr ("[" + value1 + " + " + offset.GetValue + " * " + value2 + " "  + "]")

type Label = string


//accumulator
let eax = Reg.Reg("%eax")
//base
let ebx = Reg.Reg("%ebx")
//counter
let ecx = Reg.Reg("%ecx")
//data
let edx = Reg.Reg("%edx")
//source index
let esi = Reg.Reg("%esi")
//destination index
let edi = Reg.Reg("%edi")
//base pointer
let ebp = Reg.Reg("%ebp")
//stack pointer
let esp = Reg.Reg("%esp")
//instruction pointer
let eip = Reg.Reg("%eip")
//al
let al = Reg.Reg("al")

type MemReg =
  | Mem of Mem
  | Reg of Reg
  | Ptr of Ptr
  | PtrReg of Reg * Offset
  | BytePtrReg  of Reg * Offset
  | WordPtrReg  of Reg * Offset
  | DWordPtrReg of Reg * Offset
  with 
    member this.Sum offset = 
      match this with
      | PtrReg (reg, offset') -> PtrReg (reg, offset + offset')
        
    member this.GetValue = 
      match this with      
      | Ptr (Ptr.Ptr value) -> value
      | PtrReg (reg, offset) -> reg.PtrWithOffset offset
      | Mem value -> value.GetValue
      | Reg value -> value.GetValue
      | BytePtrReg (reg, offset)  -> "BYTE PTR " + reg.PtrWithOffset offset
      | WordPtrReg (reg, offset)  -> "WORD PTR " + reg.PtrWithOffset offset
      | DWordPtrReg (reg, offset) -> "DWORD PTR " + reg.PtrWithOffset offset  
    member this.GetRegAddress =
      match this with
      | Reg reg -> PtrReg(reg, 0)
    static member EAX = 
      Reg eax
    static member EBX = 
      Reg ebx
    static member ECX = 
      Reg ecx
    static member EDX = 
      Reg edx
    static member ESI = 
      Reg esi
    static member EDI = 
      Reg edi
    static member EBP = 
      Reg ebp
    static member ESP = 
      Reg esp
    static member EIP = 
      Reg eip


and MemRegConst =
  | MemOrReg  of MemReg
  | Const     of Const
  with 
    member this.GetValue = 
      match this with
      | MemOrReg value -> value.GetValue
      | Const value -> value.GetValue

    member this.GetRegAddress =
      match this with
      | MemOrReg (Reg reg) -> MemOrReg(PtrReg(reg, 0))

    static member ConstOfInt _int =
      Const (Const.Const (string _int))

    static member ConstOfString _string =
      Const (Const.Const _string)

    static member ConstOfBool (_bool : bool) =
      Const (Const.Const (string _bool))

    static member EAX = 
      MemOrReg (MemReg.Reg eax)
    static member EBX = 
      MemOrReg (MemReg.Reg ebx)
    static member ECX = 
      MemOrReg (MemReg.Reg ecx)
    static member EDX = 
      MemOrReg (MemReg.Reg edx)
    static member ESI = 
      MemOrReg (MemReg.Reg esi)
    static member EDI = 
      MemOrReg (MemReg.Reg edi)
    static member EBP = 
      MemOrReg (MemReg.Reg ebp)
    static member ESP = 
      MemOrReg (MemReg.Reg esp)
    static member EIP = 
      MemOrReg (MemReg.Reg eip)
    static member AL =
      MemOrReg (MemReg.Reg al)


let empty_state print_string_nl global_symbol = 
  { GlobalSymbols = global_symbol;
    NumTabs = 0; 
    Consts = ResizeArray()
    print_string_nl = print_string_nl
//    IsMethodVoid = false
    ActualArgs   = -1 }
//
//    GlobalSymbols       : GlobalSymbols
//    NumTabs           : int
//    print_string      : string -> Unit
//    print_string_nl   : string -> Unit
//    IsMethodVoid      : bool
//    ActualArgs        : int

type ParseState<'a> = State<'a,Environment>

